Skip to content

Make airflow dags test wait for Human-in-the-loop input instead of hanging#68492

Merged
vatsrahul1001 merged 2 commits into
apache:mainfrom
astronomer:dags-test-awaiting-input
Jun 28, 2026
Merged

Make airflow dags test wait for Human-in-the-loop input instead of hanging#68492
vatsrahul1001 merged 2 commits into
apache:mainfrom
astronomer:dags-test-awaiting-input

Conversation

@kaxil

@kaxil kaxil commented Jun 13, 2026

Copy link
Copy Markdown
Member

A task that reaches the awaiting_input state (Human-in-the-loop, #68028) hangs airflow dags test forever: nothing can resume it, so the command logs No tasks to run. unrunnable tasks: ... once per second indefinitely. The in-process task runner also swallows SIGTERM, so timeout cannot stop it.

dag.test() now treats parked HITL tasks as waiting rather than unrunnable, and never resolves them itself: the run stays alive, logging which tasks await input, until a response recorded from outside flips them back to SCHEDULED -- the existing loop then resumes them. This is the same contract a parked task has on a real deployment, where the API response handler or the scheduler's timeout sweep performs the resume.

Because the resume is the standard transition, the existing response channels work with dags test unchanged: the Required Actions UI or the HITL REST API of an api-server sharing the metadata database (e.g. airflow standalone). Verified end to end in breeze: airflow dags test parks and logs Waiting for Human-in-the-loop input for tasks: ['wait_approval'], a PATCH .../hitlDetails with {"chosen_options": ["Approve"]} against a locally started airflow api-server resumes it, the downstream task runs, and the run finishes successfully. This also gives AI agents a clean way to drive HITL pipelines locally: run dags test, surface the request to a human, submit the answer through the API.

The change is deliberately minimal -- one hunk in the dag.test() loop plus a contract test. Interactive console prompting in the dags test CLI (and any API additions it needs) is left for follow-up PRs.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Comment thread airflow-core/docs/tutorial/hitl.rst Outdated

@shahar1 shahar1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add an AI usage statement :)

Comment thread task-sdk/src/airflow/sdk/definitions/dag.py
@kaxil

kaxil commented Jun 14, 2026

Copy link
Copy Markdown
Member Author

Please add an AI usage statement :)

Dropped an email reply on the dev list around it, thanks.

@Dev-iL Dev-iL left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to have cli flags like --hitl-auto-approve and --hitl-auto-reject so users can test hitl dags without actually interacting with them?

@kaxil kaxil force-pushed the dags-test-awaiting-input branch from 958db37 to 5d936ed Compare June 27, 2026 19:38
kaxil added 2 commits June 27, 2026 20:39
…nging

Previously a HITL task that parked in awaiting_input made `airflow dags
test` loop "No tasks to run. unrunnable tasks: ..." once per second
forever, with no way to make progress (the in-process runner also
swallows SIGTERM, so even `timeout` could not stop it).

dag.test() now treats parked HITL tasks as waiting rather than
unrunnable, and never resolves them itself: the run stays alive, logging
which tasks await input, until a response recorded from outside flips
them back to SCHEDULED -- at which point the existing loop resumes them.
This matches how a parked task behaves on a real deployment, and the
existing response channels work unchanged: the Required Actions UI or
the HITL REST API (PATCH .../hitlDetails) of an api-server sharing the
metadata database (e.g. airflow standalone). Humans and AI agents can
drive HITL pipelines locally by running dags test and submitting the
response through that API.

Interactive console prompting in the dags test CLI is left for a
follow-up.
@kaxil kaxil force-pushed the dags-test-awaiting-input branch from 5d936ed to 6cccc82 Compare June 27, 2026 19:39
@kaxil kaxil added the backport-to-v3-3-test Backport to v3-3-test label Jun 27, 2026
@kaxil kaxil added this to the Airflow 3.3.0 milestone Jun 27, 2026
@vatsrahul1001 vatsrahul1001 merged commit 63cee22 into apache:main Jun 28, 2026
102 checks passed
@vatsrahul1001 vatsrahul1001 deleted the dags-test-awaiting-input branch June 28, 2026 12:20
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-3-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

Status Branch Result
v3-3-test PR Link

shahar1 pushed a commit that referenced this pull request Jun 28, 2026
…nstead of hanging (#68492) (#69104)

* Make airflow dags test wait for Human-in-the-loop input instead of hanging

Previously a HITL task that parked in awaiting_input made `airflow dags
test` loop "No tasks to run. unrunnable tasks: ..." once per second
forever, with no way to make progress (the in-process runner also
swallows SIGTERM, so even `timeout` could not stop it).

dag.test() now treats parked HITL tasks as waiting rather than
unrunnable, and never resolves them itself: the run stays alive, logging
which tasks await input, until a response recorded from outside flips
them back to SCHEDULED -- at which point the existing loop resumes them.
This matches how a parked task behaves on a real deployment, and the
existing response channels work unchanged: the Required Actions UI or
the HITL REST API (PATCH .../hitlDetails) of an api-server sharing the
metadata database (e.g. airflow standalone). Humans and AI agents can
drive HITL pipelines locally by running dags test and submitting the
response through that API.

Interactive console prompting in the dags test CLI is left for a
follow-up.

* Document the HITL REST API calls for responding during dags test
(cherry picked from commit 63cee22)

Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
Co-authored-by: Rahul Vats <43964496+vatsrahul1001@users.noreply.github.com>
karenbraganz pushed a commit to karenbraganz/airflow that referenced this pull request Jun 30, 2026
…nging (apache#68492)

* Make airflow dags test wait for Human-in-the-loop input instead of hanging

Previously a HITL task that parked in awaiting_input made `airflow dags
test` loop "No tasks to run. unrunnable tasks: ..." once per second
forever, with no way to make progress (the in-process runner also
swallows SIGTERM, so even `timeout` could not stop it).

dag.test() now treats parked HITL tasks as waiting rather than
unrunnable, and never resolves them itself: the run stays alive, logging
which tasks await input, until a response recorded from outside flips
them back to SCHEDULED -- at which point the existing loop resumes them.
This matches how a parked task behaves on a real deployment, and the
existing response channels work unchanged: the Required Actions UI or
the HITL REST API (PATCH .../hitlDetails) of an api-server sharing the
metadata database (e.g. airflow standalone). Humans and AI agents can
drive HITL pipelines locally by running dags test and submitting the
response through that API.

Interactive console prompting in the dags test CLI is left for a
follow-up.

* Document the HITL REST API calls for responding during dags test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants